import * as mysql from 'mysql2'
import { DbConfig } from './db-config'
import { DbField } from './db-field'
import { DbDialect } from './db-dialect'

const NOT_NULL_FLAG_MASK = 1             //          00000001
const PRI_KEY_FLAG_MASK = 2              //          00000010
const UNIQUE_KEY_FLAG_MASK = 4           //          00000100
const MULTIPLE_KEY_FLAG_MASK = 8         // 00000000 00001000
const BLOB_FLAG_MASK = 16                // 00000000 00010000
const UNSIGNED_FLAG_MASK = 32            // 00000000 00100000
const ZEROFILL_FLAG_MASK = 64            // 00000000 01000000
const BINARY_FLAG_MASK = 128             // 00000000 10000000
const ENUM_FLAG_MASK = 256               // 00000001 00000000
const AUTO_INCREMENT_FLAG_MASK = 512     // 00000010 00000000
const TIMESTAMP_FLAG_MASK = 1024         // 00000100 00000000
const SET_FLAG_MASK = 2048               // 00001000 00000000
const NO_DEFAULT_VALUE_FLAG_MASK = 4096  // 00010000 00000000
const ON_UPDATE_NOW_FLAG_MASK = 8192     // ‭001000000 0000000‬
const PART_KEY_FLAG_MASK = 16384         // 01000000 00000000
const GROUP_FLAG_MASK = 32768            // 10000000 00000000
const UNIQUE_FLAG_MASK = 65536
const BINCMP_FLAG_MASK = 131072

const typeMapping = {
    1: 'tinyint',
    2: 'smallint',
    3: 'int',
    4: 'float',
    5: 'double',
    7: 'timestamp',
    8: 'bigint',
    10: 'date',
    11: 'time',
    12: 'datetime',
    246: 'decimal',
    252: 'longtext',
    253: 'varchar',
    254: 'char',
}

function unknownType(table, field) {
    return `Unknown database type: ${table} ${field.name} ${JSON.stringify(field)}`
}

export class MysqlDialect implements DbDialect {
    private connection
    private destroy = false

    constructor(config: DbConfig) {
        this.connection = mysql.createConnection({
            host: config.host || 'localhost',
            port: config.port || 3306,
            user: config.username || 'root',
            password: config.password,
            database: config.database
        })
        this.connection.connect()
    }

    public tables(): Promise<string[]> {
        const conn = this.connection
        return new Promise<string[]>((resolve, reject) => {
            conn.query('show tables', (err, results, fields) => {
                if (err) {
                    reject(err)
                } else {
                    const ret: string[] = []
                    const col = fields[0].name
                    for (const row of results) {
                        ret.unshift(row[col])
                    }
                    resolve(ret)
                }
            })
        })
    }

    // https://dev.mysql.com/doc/dev/mysql-server/8.0.11/group__group__cs__column__definition__flags.html
    public tableFields(tableName: string): Promise<DbField[]> {
        const conn = this.connection
        return new Promise<DbField[]>((resolve, reject) => {
            conn.query(`select * from ${tableName} limit 0`, (err, results, fields) => {
                if (err) {
                    reject(err)
                } else {
                    const keys: DbField[] = []
                    const ret: DbField[] = []
                    for (const f of fields) {
                        const type = typeMapping[f.columnType] || reject(unknownType(tableName, f))
                        const field = new DbField({
                            name: f.name,
                            table: f.table,
                            type,
                            length: f.columnLength,
                            // tslint:disable-next-line:no-bitwise
                            primary: (f.flags & PRI_KEY_FLAG_MASK) > 0
                        })
                        if (field.primary) {
                            keys.push(field)
                        } else {
                            ret.push(field)
                        }
                    }
                    resolve([...keys, ...ret])
                }
            })
        })
    }

    public dispose() {
        if (this.connection) {
            this.connection.end()
            this.connection = null
        }
        this.destroy = true
    }
}


async function test() {
    const dialect = new MysqlDialect({
        host: 'localhost',
        username: 'root',
        password: '123456',
        database: 'spring-code-generator-test'
    })
    try {
        const ret = await dialect.tables()

        const fields = await dialect.tableFields('test')
        // console.log(fields)
    } finally {
        dialect.dispose()
    }
}

// test()
